package com.exam.api.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @Author 北橙
 * @Create 2023/5/12
 * @Description 分布式锁助手
 */
@Slf4j
public class DistributedLockHelper {


    /**
     * 默认等待获取锁时间3秒
     */
    public static final long LOCK_HELPER_WAIT_TIME = 3;
    /**
     * 默认等待锁时间2秒
     */
    public static final long LOCK_HELPER_LEASE_TIME = 2;


    /**
     * 默认获取锁等待时间 3秒 业务超时时间 2秒
     *
     * @param lock           锁
     * @param businessLogic  业务逻辑函数
     * @param noLockAcquired 未能成功获取锁回调函数
     * @param <R>            业务回调函数结果类型
     * @return 返回业务回调函数结果
     */
    public static <R> R tryLock(RLock lock, Supplier<R> businessLogic, Supplier<R> noLockAcquired) throws InterruptedException {
        return tryLock(lock, LOCK_HELPER_WAIT_TIME, LOCK_HELPER_LEASE_TIME, TimeUnit.SECONDS, businessLogic, noLockAcquired);
    }

    /**
     * 默认获取锁等待时间 3秒 业务超时时间 2秒
     *
     * @param lock          锁
     * @param businessLogic 业务逻辑函数
     * @param theLockFailed 未能成功获取锁回调函数
     * @param <R>           业务回调函数结果类型
     * @return 返回业务回调函数结果
     */
    public static <R> R tryLock(RLock lock, Supplier<R> businessLogic, Function<InterruptedException, R> theLockFailed) {
        return tryLock(lock, LOCK_HELPER_WAIT_TIME, LOCK_HELPER_LEASE_TIME, TimeUnit.SECONDS, businessLogic, theLockFailed);
    }

    /**
     * 获取分布式锁
     *
     * @param lock           锁
     * @param waitTime       获取锁等待时间
     * @param leaseTime      业务超时时间
     * @param unit           时间单位
     * @param businessLogic  业务逻辑函数
     * @param noLockAcquired 未能成功获取锁回调函数
     * @param <R>            业务回调函数结果类型
     * @return 返回业务回调函数结果
     */
    public static <R> R tryLock(RLock lock, long waitTime, long leaseTime, TimeUnit unit, Supplier<R> businessLogic, Supplier<R> noLockAcquired) throws InterruptedException {
        try {
            registerTransactionReleaseLock(lock);
            if (lock.tryLock(waitTime, leaseTime, unit)) {
                return businessLogic.get();
            }
        } finally {
            releaseLock(lock);
        }
        return noLockAcquired.get();
    }

    /**
     * 获取分布式锁
     *
     * @param lock          锁
     * @param waitTime      获取锁等待时间
     * @param leaseTime     业务超时时间
     * @param unit          时间单位
     * @param businessLogic 业务逻辑函数
     * @param theLockFailed 未能成功获取锁回调函数或者被中断
     * @param <R>           业务回调函数结果类型
     * @return 返回业务回调函数结果
     */
    public static <R> R tryLock(RLock lock, long waitTime, long leaseTime, TimeUnit unit, Supplier<R> businessLogic, Function<InterruptedException, R> theLockFailed) {
        try {
            registerTransactionReleaseLock(lock);
            if (lock.tryLock(waitTime, leaseTime, unit)) {
                return businessLogic.get();
            }
            return theLockFailed.apply(new InterruptedException("尝试获取锁失败"));
        } catch (InterruptedException e) {
            return theLockFailed.apply(e);
        } finally {
            releaseLock(lock);
        }
    }

    public static void releaseLock(RLock lock) {
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            log.info(" <<<=== 分布式锁 本次分布式锁在事务中无需普通释放锁 锁名称 : {}", lock.getName());
            return;
        }
        if (!lock.isHeldByCurrentThread()) {
            log.info(" <<<=== 分布式锁 本锁不是当前线程的锁 锁名称 : {} 线程Id : {} 线程名称 : {}", lock.getName(), Thread.currentThread().getId(), Thread.currentThread().getName());
            return;
        }
        lock.unlock();
        log.info(" <<<=== 分布式锁  释放锁成功 锁名称 : {}", lock.getName());
    }

    public static void registerTransactionReleaseLock(RLock lock) {
        if (!TransactionSynchronizationManager.isActualTransactionActive()) {
            log.info(" <<<=== 事务中的分布式锁 本次分布式锁没有在事务无需注册 TransactionSynchronization 锁名称 : {}", lock.getName());
            return;
        }
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCompletion(int status) {
                TransactionSynchronization.super.afterCompletion(status);
                if (!lock.isHeldByCurrentThread()) {
                    log.info(" <<<=== 事务中的分布式锁 本锁不是当前线程的锁 锁名称 : {} 线程Id : {} 线程名称 : {}", lock.getName(), Thread.currentThread().getId(), Thread.currentThread().getName());
                }
                lock.unlock();
                log.info(" <<<=== 事务中的分布式锁  释放锁成功 锁名称 : {}", lock.getName());
            }
        });
        log.info(" <<<=== 事务中的分布式锁  注册成功 锁名称 : {}", lock.getName());
    }
}
